package cn.zbacm.api.application.service;

import cn.zbacm.api.application.dto.MenuApp;
import cn.zbacm.api.application.dto.MenuGroup;
import cn.zbacm.api.application.dto.MenuItem;
import cn.zbacm.api.application.entity.ApplicationGroup;
import cn.zbacm.api.application.entity.ApplicationRole;
import cn.zbacm.api.application.repository.ApplicationGroupRepository;
import cn.zbacm.api.application.repository.ApplicationRoleRepository;
import cn.zbacm.api.user.entity.User;
import cn.zbacm.api.user.entity.UserRole;
import cn.zbacm.api.user.repository.UserRoleRepository;
import cn.zbacm.api.util.RandomGenerator;
import com.google.common.collect.Lists;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class ApplicationService {
  @Autowired
  private ApplicationGroupRepository applicationGroupRepository;

  @Autowired
  private ApplicationRoleRepository applicationRoleRepository;

  @Autowired
  private UserRoleRepository userRoleRepository;

  @Autowired
  private ModelMapper modelMapper;

  private MenuGroup mapApplicationGroupToMenuGroup(ApplicationGroup applicationGroup) {
    MenuGroup menuGroup = modelMapper.map(applicationGroup, MenuGroup.class);
    menuGroup.setPath("#" + RandomGenerator.generateRandomNumbers(5));
    return menuGroup;
  }

  private MenuApp mapApplicationRoleToMenuApp(ApplicationRole applicationRole) {
    MenuApp menuApp = modelMapper.map(applicationRole, MenuApp.class);
    String path = applicationRole.getApplication().getUrl();
    String parameter = applicationRole.getParameter();
    if (StringUtils.hasText(parameter)) {
      path = path.concat("/" + parameter);
    }
    menuApp.setPath(path);
    return menuApp;
  }

  private Map<Integer, List<MenuApp>> getGroupApplicationsMap(
          List<ApplicationRole> applicationRoles) {
    Map<Integer, List<MenuApp>> map = new HashMap<>();
    applicationRoles.forEach(
            applicationRole -> {
              int groupId = applicationRole.getApplication().getGroupId();
              List<MenuApp> groupApps = map.get(groupId);
              if (groupApps == null) {
                groupApps = new ArrayList<>();
              }
              MenuApp menuApp = mapApplicationRoleToMenuApp(applicationRole);
              if (!groupApps.contains(menuApp)) {
                groupApps.add(menuApp);
              }
              map.put(groupId, groupApps);
            });
    map.values().forEach(apps -> apps.sort(Comparator.comparingInt(MenuApp::getDisplayOrder)));
    return map;
  }

  private MenuGroup setChildrenRecursively(
          MenuGroup entry,
          Map<Integer, List<MenuGroup>> groupChildrenMap,
          Map<Integer, List<MenuApp>> groupApplicationsMap) {
    List<MenuGroup> groupChildren = groupChildrenMap.get(entry.getId());
    List<MenuApp> groupApps = groupApplicationsMap.get(entry.getId());

    if (groupChildren != null) {
      for (int i = 0; i < groupChildren.size(); i++) {
        MenuGroup child = groupChildren.get(i);
        groupChildren.set(i, setChildrenRecursively(child, groupChildrenMap, groupApplicationsMap));
      }
      groupChildren.removeAll(Collections.singleton(null));
      groupChildren.sort(Comparator.comparingInt((MenuGroup::getDisplayOrder)));
      List<MenuItem> children = Lists.newArrayList(groupChildren);
      if (groupApps != null) {
        children.addAll(groupApps);
      }
      entry.setChildren(children);
      return entry;
    }

    // 分组下既无分组也无应用，不返回
    if (groupApps == null) {
      return null;
    }

    entry.setChildren(groupApps);
    return entry;
  }

  public List<MenuGroup> generateUserMenu(User user) {
    // 获取用户的应用
    List<UserRole> userRoles = userRoleRepository.findByUserId(user.getId());
    if (userRoles.isEmpty()) {
      return new ArrayList<>();
    }
    List<Integer> roleIds =
            userRoles.stream().map(UserRole::getRoleId).collect(Collectors.toList());
    List<ApplicationRole> applicationRoles =
            applicationRoleRepository.findWithApplicationByRoleIdIn(roleIds);

    // 将用户的应用组成菜单
    Iterable<ApplicationGroup> applicationGroups = applicationGroupRepository.findAll();
    Map<Integer, List<MenuApp>> groupApplicationsMap = getGroupApplicationsMap(applicationRoles);
    Map<Integer, List<MenuGroup>> groupChildrenMap = new HashMap<>();
    List<MenuGroup> entries = new ArrayList<>(); // 按照现有设计，只会有一个根节点入口
    applicationGroups.forEach(
            applicationGroup -> {
              Integer parentId = applicationGroup.getParentId();
              if (parentId == null) {
                MenuGroup entry = mapApplicationGroupToMenuGroup(applicationGroup);
                entries.add(entry);
                return;
              }
              List<MenuGroup> parentChildren = groupChildrenMap.get(parentId);
              if (parentChildren == null) {
                parentChildren = new ArrayList<>();
              }
              parentChildren.add(mapApplicationGroupToMenuGroup(applicationGroup));
              groupChildrenMap.put(parentId, parentChildren);
            });

    for (int i = 0; i < entries.size(); i++) {
      MenuGroup entry = entries.get(i);
      entries.set(i, setChildrenRecursively(entry, groupChildrenMap, groupApplicationsMap));
    }
    entries.sort(Comparator.comparingInt((MenuGroup::getDisplayOrder)));
    return entries;
  }
}
